///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __ShadowCameraSetupFocused_H__
#define __ShadowCameraSetupFocused_H__

#include "OgrePrerequisites.h"
#include "OgreShadowCameraSetup.h"
#include "OgrePolygon.h"
#include "OgreConvexBody.h"


namespace Ogre {

	class ConvexBody;

	
	
	
	class _OgreExport FocusedShadowCameraSetup : public ShadowCameraSetup
	{
	protected:
		
		static const Matrix4 msNormalToLightSpace;
		static const Matrix4 msLightSpaceToNormal;

		
		Frustum* mTempFrustum;

		
		Camera* mLightFrustumCamera;
		mutable bool mLightFrustumCameraCalculated;

		/// Use tighter focus region?
		bool mUseAggressiveRegion;

		
		class _OgreExport PointListBody
		{
			Polygon::VertexList mBodyPoints;
			AxisAlignedBox		mAAB;

		public:
			PointListBody();
			PointListBody(const ConvexBody& body);
			~PointListBody();

			
			void merge(const PointListBody& plb);

			
			void build(const ConvexBody& body, bool filterDuplicates = true);

			
			void buildAndIncludeDirection(const ConvexBody& body, 
				Real extrudeDist, const Vector3& dir);

			
			const AxisAlignedBox& getAAB(void) const;	

			
			void addPoint(const Vector3& point);

			
			void addAAB(const AxisAlignedBox& aab);

			
			const Vector3& getPoint(size_t cnt) const;

			
			size_t getPointCount(void) const;

			
			void reset(void);

		};

		// Persistent calculations to prevent reallocation
		mutable ConvexBody mBodyB;
		mutable PointListBody mPointListBodyB;
		mutable PointListBody mPointListBodyLVS;

	protected:
		
		void calculateShadowMappingMatrix(const SceneManager& sm, const Camera& cam, 
			const Light& light, Matrix4 *out_view, 
			Matrix4 *out_proj, Camera *out_cam) const;

		
		void calculateB(const SceneManager& sm, const Camera& cam, const Light& light, 
			const AxisAlignedBox& sceneBB, const AxisAlignedBox& receiverBB, PointListBody *out_bodyB) const;

		
		void calculateLVS(const SceneManager& sm, const Camera& cam, const Light& light,
			const AxisAlignedBox& sceneBB, PointListBody *out_LVS) const;

		/**	Returns the projection view direction.
		@remarks
		After the matrix L_p is applied the orientation of the light space may tilt for
		non-identity projections. To prevent a false shadow cast the real view direction
		is evaluated and applied to the light matrix L.
		@param lightSpace: matrix of the light space transformation
		@param cam: current viewer camera
		@param bodyLVS: intersection body LVS (relevant space in front of the camera)
		*/
		Vector3 getLSProjViewDir(const Matrix4& lightSpace, const Camera& cam, 
			const PointListBody& bodyLVS) const;

		
		Vector3 getNearCameraPoint_ws(const Matrix4& viewMatrix, 
			const PointListBody& bodyLVS) const;

		
		Matrix4 transformToUnitCube(const Matrix4& m, const PointListBody& body) const;

		
		Matrix4 buildViewMatrix(const Vector3& pos, const Vector3& dir, const Vector3& up) const;

	public:
		
		FocusedShadowCameraSetup(void);

		
		virtual ~FocusedShadowCameraSetup(void);

		
		virtual void getShadowCamera(const SceneManager *sm, const Camera *cam, 
			const Viewport *vp, const Light *light, Camera *texCam, size_t iteration) const;

		
		void setUseAggressiveFocusRegion(bool aggressive) { mUseAggressiveRegion = aggressive; }

		bool getUseAggressiveFocusRegion() const { return mUseAggressiveRegion; }

	};

	
	

}

#endif
